package cn.iocoder.yudao.module.biz.service.contract;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.biz.dal.dataobject.contract.autocode.AutoCodePartDO;
import cn.iocoder.yudao.module.biz.dal.dataobject.contract.autocode.AutoCodeResultDO;
import cn.iocoder.yudao.module.biz.dal.dataobject.contract.autocode.AutoCodeRuleDO;
import cn.iocoder.yudao.module.biz.dal.dataobject.contract.receive.ReceiveRecordDO;
import cn.iocoder.yudao.module.biz.dal.dataobject.contract.transformer.TransformerDO;
import cn.iocoder.yudao.module.biz.dal.mysql.contract.autocode.AutoCodePartMapper;
import cn.iocoder.yudao.module.biz.dal.mysql.contract.autocode.AutoCodeResultMapper;
import cn.iocoder.yudao.module.biz.dal.mysql.contract.autocode.AutoCodeRuleMapper;
import cn.iocoder.yudao.module.biz.dal.mysql.contract.receive.ReceiveRecordMapper;
import cn.iocoder.yudao.module.biz.dal.mysql.contract.transformer.TransformerMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;

import java.util.*;
import cn.iocoder.yudao.module.biz.controller.admin.contract.vo.*;
import cn.iocoder.yudao.module.biz.dal.dataobject.contract.ContractArchiveDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.biz.dal.mysql.contract.ContractArchiveMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;

import static cn.iocoder.yudao.module.biz.enums.ErrorCodeConstants.*;

/**
 * 合同档案 Service 实现类
 *
 * @author 超级管理员
 */
@Service
@Validated
public class ContractArchiveServiceImpl implements ContractArchiveService {

    private static final String PART_TYPE_FIXEDCHAR = "FIXEDCHAR";
    private static final String PART_TYPE_DATETIME = "DATETIME";
    private static final String PART_TYPE_SERIALNUMBER = "SERIALNUMBER";

    public static final String CYCLE_METHOD_YEAR = "YEAR";
    public static final String CYCLE_METHOD_MONTH = "MONTH";
    public static final String CYCLE_METHOD_DAY = "DAY";


    @Resource
    private ContractArchiveMapper contractArchiveMapper;

    @Resource
    private TransformerMapper transformerMapper;

    @Resource
    private ReceiveRecordMapper receiveRecordMapper;

    @Resource
    private AutoCodePartMapper autoCodePartMapper;
    @Resource
    private AutoCodeResultMapper autoCodeResultMapper;
    @Resource
    private AutoCodeRuleMapper autoCodeRuleMapper;


    @Override
    public Long createContractArchive(ContractArchiveSaveReqVO createReqVO) {
        // 插入
        ContractArchiveDO contractArchive = BeanUtils.toBean(createReqVO, ContractArchiveDO.class);
        contractArchiveMapper.insert(contractArchive);
        // 插入变压器列表
        if(!createReqVO.getTransformerList().isEmpty()) {
            List<TransformerDO> transformerList = createReqVO.getTransformerList();
            transformerList.forEach(transformer -> {
                transformer.setContractArchiveId(contractArchive.getId());
            });
            transformerMapper.insertBatch(transformerList);
        }
        // 插入应收款记录列表
        if(!createReqVO.getReceiveRecordList().isEmpty()) {
            List<ReceiveRecordDO> receiveRecordList = createReqVO.getReceiveRecordList();
            receiveRecordList.forEach(receiveRecord -> {
                receiveRecord.setContractArchiveId(contractArchive.getId());
            });
            receiveRecordMapper.insertBatch(receiveRecordList);
        }
        // 返回
        return contractArchive.getId();
    }

    @Override
    public void updateContractArchive(ContractArchiveSaveReqVO updateReqVO) {
        // 校验存在
        validateContractArchiveExists(updateReqVO.getId());
        // 更新
        ContractArchiveDO updateObj = BeanUtils.toBean(updateReqVO, ContractArchiveDO.class);
        contractArchiveMapper.updateById(updateObj);
        // 删除并重新插入变压器列表
        if(!updateReqVO.getTransformerList().isEmpty()) {
            transformerMapper.deleteByContractArchiveId(updateReqVO.getId());
            List<TransformerDO> transformerList = updateReqVO.getTransformerList();
            transformerList.forEach(transformer -> {
                transformer.setContractArchiveId(updateReqVO.getId());
            });
            transformerMapper.insertBatch(transformerList);
        }
        // 删除并重新插入应收款记录列表
        if(!updateReqVO.getReceiveRecordList().isEmpty()) {
            receiveRecordMapper.deleteByContractArchiveId(updateReqVO.getId());
            List<ReceiveRecordDO> receiveRecordList = updateReqVO.getReceiveRecordList();
            receiveRecordList.forEach(receiveRecord -> {
                receiveRecord.setContractArchiveId(updateReqVO.getId());
            });
            receiveRecordMapper.insertBatch(receiveRecordList);
        }
    }

    @Override
    public void deleteContractArchive(Long id) {
        // 校验存在
        validateContractArchiveExists(id);
        // 删除合同相关变压器列表
        transformerMapper.deleteByContractArchiveId(id);
        // 删除合同相关应收款列表
        receiveRecordMapper.deleteByContractArchiveId(id);
        // 删除
        contractArchiveMapper.deleteById(id);

    }

    @Override
    public void deleteContractArchiveListByIds(List<Long> ids) {
        for(Long id : ids) {
            validateContractArchiveExists(id);
            // 删除合同相关变压器列表
            transformerMapper.deleteByContractArchiveId(id);
            // 删除合同相关应收款列表
            receiveRecordMapper.deleteByContractArchiveId(id);
        }
        // 删除
        contractArchiveMapper.deleteByIds(ids);
    }


    private void validateContractArchiveExists(Long id) {
        if (contractArchiveMapper.selectById(id) == null) {
            throw exception(BIZ_CONTRACT_ARCHIVE_NOT_EXISTS);
        }
    }

    @Override
    public ContractArchiveDO getContractArchive(Long id) {
        //return contractArchiveMapper.selectById(id);
        return contractArchiveMapper.getContractArchiveDetail(id);
    }

    @Override
    public PageResult<ContractArchiveDO> getContractArchivePage(ContractArchivePageReqVO pageReqVO) {
        return contractArchiveMapper.selectPage(pageReqVO);
    }

    @Override
    public void changeStatus(ContractArchiveSaveReqVO updateReqVO) {
        // 校验存在
        validateContractArchiveExists(updateReqVO.getId());
        // 更新
        updateReqVO.setAuditorId(SecurityFrameworkUtils.getLoginUserId());
        ContractArchiveDO updateObj = BeanUtils.toBean(updateReqVO, ContractArchiveDO.class);
        if("2".equals(updateReqVO.getStatus())) {
            updateObj.setAuditFailedReason("");
        }
        contractArchiveMapper.updateById(updateObj);
        if("2".equals(updateReqVO.getStatus())) {
            // 生效收款记录
            ReceiveRecordDO receiveRecordDO = new ReceiveRecordDO();
            receiveRecordDO.setContractArchiveId(updateReqVO.getId());
            receiveRecordDO.setIsValid("1");
            receiveRecordMapper.updateStatusByContractId(receiveRecordDO);
        }
    }

    @Override
    public void renewContractArchive(ContractArchiveSaveReqVO updateReqVO) {
        // 校验合同是否已为待续签状态
        ContractArchiveDO originalDo = contractArchiveMapper.getContractArchiveDetail(updateReqVO.getId());
        if(null != originalDo && "3".equals(originalDo.getStatus())) {
            // 变更原始合同状态为停止
            originalDo.setStatus("4");
            contractArchiveMapper.updateById(originalDo);
            // 新增续签的合同
            updateReqVO.setId(null);
            updateReqVO.setIsRenew("1");
            updateReqVO.setOriginalContractId(originalDo.getId());
            updateReqVO.setStatus("1");
            createContractArchive(updateReqVO);
        }
    }

    @Override
    public List<ContractArchiveDO> getExpiredContractArchiveList() {
        return contractArchiveMapper.getExpiredContractArchiveList();
    }

    @Override
    public void changeStatusToRenew(Long id) {
        contractArchiveMapper.changeStatusToRenew(id);
    }

    /**
     * 获得自动生成的合同编号
     * @param ruleCode
     * @return
     */
    @Override
    public String getAutoGenerateCode(String ruleCode) {
        return genSerialCodeByCode(ruleCode);
    }


    /**
     * 生成流水号
     *
     * @param ruleCode 规则编码
     * @return 流水号
     */
    public String genSerialCodeByCode(String ruleCode) {

        // 获取规则
        AutoCodeRuleDO autoCodeRule = autoCodeRuleMapper.selectOne(
                Wrappers.lambdaQuery(AutoCodeRuleDO.class)
                        .eq(AutoCodeRuleDO::getRuleCode, ruleCode)
        );
        // 获取规则下的所有规则段
        List<AutoCodePartDO> autoCodePartList = autoCodePartMapper.selectList(
                Wrappers.lambdaQuery(AutoCodePartDO.class)
                        .eq(AutoCodePartDO::getRuleId, autoCodeRule.getRuleId())
                        .orderByAsc(AutoCodePartDO::getPartIndex)
        );
        StringBuilder serialCode = new StringBuilder();
        // 循环规则段
        for (AutoCodePartDO autoCodePart : autoCodePartList) {
            switch (autoCodePart.getPartType()) {
                case PART_TYPE_FIXEDCHAR:
                    // 固定字符
                    serialCode.append(autoCodePart.getFixedCharacter());
                    break;
                case PART_TYPE_DATETIME:
                    // 日期时间
                    String dateFormat = autoCodePart.getDateFormat();
                    serialCode.append(DateUtil.format(new Date(), dateFormat));
                    break;
                case PART_TYPE_SERIALNUMBER:
                    // 流水号
                    serialCode.append(genSerialNumber(autoCodePart));
                    break;
                default:
                    break;
            }
        }
        return serialCode.toString();
    }

    /**
     * 生成流水号
     *
     * @param autoCodePart 规则段
     * @return 流水号
     */
    private String genSerialNumber(AutoCodePartDO autoCodePart) {
        List<AutoCodeResultDO> autoCodeResultList = new ArrayList<>();
        if("1".equals(autoCodePart.getCycleFlag())) {
            switch (autoCodePart.getCycleMethod()) {
                case CYCLE_METHOD_YEAR:
                    // 固定字符
                    autoCodeResultList = genSerialNumberByYear(autoCodePart);
                    break;
                case CYCLE_METHOD_MONTH:
                    // 日期时间
                    String dateFormat = autoCodePart.getDateFormat();
                    autoCodeResultList = genSerialNumberByMonth(autoCodePart);
                    break;
                case CYCLE_METHOD_DAY:
                    // 流水号
                    autoCodeResultList = genSerialNumberByDay(autoCodePart);
                    break;
                default:
                    break;
            }
        }
        // 最新流水号
        long index;
        // 获取当前流水号
        if (autoCodeResultList != null && !autoCodeResultList.isEmpty()) {
            index = autoCodeResultList.get(0).getGenIndex() + autoCodePart.getSeriaStep();
        } else {
            // 获取流水号
            // index = autoCodePart.getSeriaStartNo();
            AutoCodeResultDO maxAutoCodeResult = genSerialNumberMax(autoCodePart);
            index = maxAutoCodeResult.getGenIndex() + autoCodePart.getSeriaStep();
        }
        // 保存流水号
        AutoCodeResultDO autoCodeResult = new AutoCodeResultDO();
        autoCodeResult.setPartId(autoCodePart.getPartId());
        autoCodeResult.setGenIndex(index);
        autoCodeResultMapper.insert(autoCodeResult);
        // 获取流水号长度
        long length = autoCodePart.getPartLength();
        // 补齐
        if (Long.toString(index).length() > length) {
            throw new RuntimeException("流水号长度超出限制");
        }
        return String.format("%0" + length + "d", index);
    }

    /**
     * 按年循环
     *
     * @param autoCodePart 规则段
     * @return 流水号记录列表
     */
    private List<AutoCodeResultDO> genSerialNumberByYear(AutoCodePartDO autoCodePart) {
        // 获取当前年的开始和结束时间
        DateTime startOfYear = DateUtil.beginOfYear(DateUtil.date());
        DateTime endOfYear = DateUtil.endOfYear(DateUtil.date());
        return autoCodeResultMapper.selectList(
                Wrappers.lambdaQuery(AutoCodeResultDO.class)
                        .eq(AutoCodeResultDO::getPartId, autoCodePart.getPartId())
                        .between(AutoCodeResultDO::getCreateTime, startOfYear, endOfYear)
                        .orderByDesc(AutoCodeResultDO::getCreateTime)
                        .orderByDesc(AutoCodeResultDO::getGenIndex)
        );
    }

    /**
     * 按月循环
     *
     * @param autoCodePart 规则段
     * @return 流水号记录列表
     */
    private List<AutoCodeResultDO> genSerialNumberByMonth(AutoCodePartDO autoCodePart) {
        // 获取当前年的开始和结束时间
        DateTime startOfMonth = DateUtil.beginOfMonth(DateUtil.date());
        DateTime endOfMonth = DateUtil.endOfMonth(DateUtil.date());
        return autoCodeResultMapper.selectList(
                Wrappers.lambdaQuery(AutoCodeResultDO.class)
                        .eq(AutoCodeResultDO::getPartId, autoCodePart.getPartId())
                        .between(AutoCodeResultDO::getCreateTime, startOfMonth, endOfMonth)
                        .orderByDesc(AutoCodeResultDO::getCreateTime)
                        .orderByDesc(AutoCodeResultDO::getGenIndex)
        );
    }

    /**
     * 按天循环
     *
     * @param autoCodePart 规则段
     * @return 流水号记录列表
     */
    private List<AutoCodeResultDO> genSerialNumberByDay(AutoCodePartDO autoCodePart) {
        // 获取当前年的开始和结束时间
        DateTime startOfDay = DateUtil.beginOfDay(DateUtil.date());
        DateTime endOfDay = DateUtil.endOfDay(DateUtil.date());
        return autoCodeResultMapper.selectList(
                Wrappers.lambdaQuery(AutoCodeResultDO.class)
                        .eq(AutoCodeResultDO::getPartId, autoCodePart.getPartId())
                        .between(AutoCodeResultDO::getCreateTime, startOfDay, endOfDay)
                        .orderByDesc(AutoCodeResultDO::getCreateTime)
                        .orderByDesc(AutoCodeResultDO::getGenIndex)
        );
    }

    /**
     * 不循环
     *
     * @param autoCodePart 规则段
     * @return 流水号记录列表
     */
    private AutoCodeResultDO genSerialNumberMax(AutoCodePartDO autoCodePart) {
        return autoCodeResultMapper.selectOne(
                Wrappers.lambdaQuery(AutoCodeResultDO.class)
                        .eq(AutoCodeResultDO::getPartId, autoCodePart.getPartId())
                        .orderByDesc(AutoCodeResultDO::getCreateTime)
                        .orderByDesc(AutoCodeResultDO::getGenIndex)
                        .last("LIMIT 1")
        );
    }
}